#ifdef _WINDOWS
#define _CRTDBG_MAP_ALLOC
#include <crtdbg.h>
#endif // _WINDOWS

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "leptjson.h"

static int main_ret = 0;
static int test_count = 0;
static int test_pass = 0;

#define EXPECT_EQ_BASE(equality, expect, actual, format) \
    do {\
        test_count++;\
        if (equality)\
            test_pass++;\
        else {\
            fprintf(stderr, "%s:%d: expect: " format " actual: " format "\n", __FILE__, __LINE__, expect, actual);\
            main_ret = 1;\
        }\
    } while(0)

#define EXPECT_EQ_INT(expect, actual) EXPECT_EQ_BASE((expect) == (actual), expect, actual, "%d")
#define EXPECT_EQ_DOUBLE(expect, actual) EXPECT_EQ_BASE((expect== actual), expect, actual, "%.17g")
#define EXPECT_EQ_STRING(expect, actual, alength) \
    EXPECT_EQ_BASE(sizeof(expect) - 1 == alength && memcmp(expect, actual, alength) == 0, expect, actual, "%s")
#define EXPECT_EQ_TRUE(actual) EXPECT_EQ_BASE(1==(actual), "true", "false", "%s")
#define EXPECT_EQ_FALSE(actual) EXPECT_EQ_BASE(0==(actual), "false", "true", "%s")

#if defined(_MSC_VER)
#define EXPECT_EQ_SIZE_T(expect, actual) EXPECT_EQ_BASE((expect) == (actual), (size_t)expect, (size_t)actual, "%Iu")
#else
#define EXPECT_EQ_SIZE_T(expect, actual) EXPECT_EQ_BASE((expect) == (actual), (size_t)expect, (size_t)actual, "%zu")
#endif


#define TEST_ERROR(error, json)\
    do{\
       lept_value v;\
       v.type = LEPT_FALSE;\
       EXPECT_EQ_INT(error, lept_parse(&v, json));\
       EXPECT_EQ_INT(LEPT_NULL, lept_get_type(&v));\
    }while(0)

#define TEST_NUMBER(expect, json)\
    do{\
       lept_value v;\
       v.type = LEPT_FALSE;\
       EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, json));\
       EXPECT_EQ_INT(LEPT_NUMBER, lept_get_type(&v));\
       EXPECT_EQ_DOUBLE(expect, lept_get_number(&v));\
    }while(0)

#define TEST_STRING(expect, json)\
    do{\
       lept_value v;\
       v.type = LEPT_FALSE;\
       EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, json));\
       EXPECT_EQ_INT(LEPT_STRING, lept_get_type(&v));\
       EXPECT_EQ_STRING(expect, lept_get_string(&v), lept_get_string_length(&v));\
    }while(0)

#define TEST_ROUNDTRIP(json)\
	do{\
		lept_value v;\
		char* json2;\
		size_t length;\
		lept_init(&v);\
		EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, json));\
		json2 = lept_stringify(&v, &length);\
		EXPECT_EQ_STRING(json, json2, length);\
		lept_free(&v);\
		free(json2);\
	}while(0)

static void test_stringify_number() {
	TEST_ROUNDTRIP("0");
	TEST_ROUNDTRIP("1.2");
}

static void test_stringify_string() {
	TEST_ROUNDTRIP("\"\"");
	TEST_ROUNDTRIP("\"123\"");
	TEST_ROUNDTRIP("\"Hello\\nWorld\"");
	TEST_ROUNDTRIP("\"\\\" \\\\ / \\b \\f \\n \\r \\t\"");
	TEST_ROUNDTRIP("null");
	TEST_ROUNDTRIP("false");
	TEST_ROUNDTRIP("true");
}

static void test_parse_null() {
	lept_value v;
	v.type = LEPT_FALSE;
	EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, "null"));
	EXPECT_EQ_INT(LEPT_NULL, lept_get_type(&v));
}

static void test_parse_false() {
	lept_value v;
	v.type = LEPT_NULL;
	EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, "false"));
	EXPECT_EQ_INT(LEPT_FALSE, lept_get_type(&v));
}

static void test_parse_true() {
	lept_value v;
	v.type = LEPT_FALSE;
	EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, "true"));
	EXPECT_EQ_INT(LEPT_TRUE, lept_get_type(&v));
}

static void test_expect_value() {
	TEST_ERROR(LEPT_PARSE_EXPECT_VALUE, "");
	TEST_ERROR(LEPT_PARSE_EXPECT_VALUE, " ");
}

static void test_invalid_value() {
	TEST_ERROR(LEPT_PARSE_INVALID_VALUE, "x");
	TEST_ERROR(LEPT_PARSE_INVALID_VALUE, "nul");
}

static void test_not_singular() {
	TEST_ERROR(LEPT_PARSE_ROOT_NOT_SINGULAR, "null x");
	TEST_ERROR(LEPT_PARSE_ROOT_NOT_SINGULAR, "false x");
	TEST_ERROR(LEPT_PARSE_ROOT_NOT_SINGULAR, "1.0 x");
	TEST_ERROR(LEPT_PARSE_ROOT_NOT_SINGULAR, "0123");
}

static void test_parse_number() {
	TEST_NUMBER(1.0, "1.0");
	TEST_NUMBER(1.03E+08, "1.03E+08");
	TEST_NUMBER(1.03E-08, "1.03E-08");
	TEST_NUMBER(-1.03E-08, "-1.03E-08");
	TEST_NUMBER(-0.5, "-0.5");
}

static void test_parse_number_too_big() {
	TEST_ERROR(LEPT_PARSE_NUMBER_TOO_BIG, "1e309");
}

static void test_parse_string() {
	TEST_STRING("hello", "\"hello\"");
	TEST_STRING("", "\"\"");
	TEST_STRING("\x24", "\"\\u0024\"");
}

static void test_parse_missing_quotation_mark() {
	TEST_ERROR(LEPT_PARSE_MISS_QUOTATION_MARK, "\"abc");
	TEST_ERROR(LEPT_PARSE_MISS_QUOTATION_MARK, "\"");
}

static void test_parse_array() {
	lept_value v;
	lept_init(&v);
	EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, "[ ]"));
	EXPECT_EQ_INT(LEPT_ARRAY, lept_get_type(&v));
	EXPECT_EQ_SIZE_T(0, lept_get_array_size(&v));
	lept_free(&v);
	
	lept_init(&v);
	EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, "[ null , null ]"));
	EXPECT_EQ_INT(LEPT_ARRAY, lept_get_type(&v));
	EXPECT_EQ_SIZE_T(2, lept_get_array_size(&v));
	lept_free(&v);

	lept_init(&v);
	EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, "[ null, false, true , 123 , \"abc\" ]"));
	EXPECT_EQ_INT(LEPT_ARRAY, lept_get_type(&v));
	EXPECT_EQ_SIZE_T(5, lept_get_array_size(&v));
	EXPECT_EQ_INT(LEPT_NULL, lept_get_type(lept_get_array_element(&v, 0)));
	EXPECT_EQ_INT(LEPT_FALSE, lept_get_type(lept_get_array_element(&v, 1)));
	EXPECT_EQ_INT(LEPT_TRUE, lept_get_type(lept_get_array_element(&v, 2)));
	EXPECT_EQ_INT(LEPT_NUMBER, lept_get_type(lept_get_array_element(&v, 3)));
	EXPECT_EQ_INT(LEPT_STRING, lept_get_type(lept_get_array_element(&v, 4)));
	EXPECT_EQ_DOUBLE(123.0, lept_get_array_element(&v, 3)->u.n);
	EXPECT_EQ_TRUE(lept_get_boolean(lept_get_array_element(&v, 2)));
	EXPECT_EQ_FALSE(lept_get_boolean(lept_get_array_element(&v, 1)));
	EXPECT_EQ_STRING("abc", lept_get_array_element(&v, 4)->u.s.s, lept_get_string_length(lept_get_array_element(&v, 4)));
	lept_free(&v);
}

static void test_parse_object() {
	lept_value v;
#if 0
	lept_init(&v);
	EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, "{}"));
	EXPECT_EQ_INT(LEPT_OBJECT, lept_get_type(&v));
	EXPECT_EQ_SIZE_T(0, lept_get_object_size(&v));
	lept_free(&v);
#endif // 0

#if 1
	lept_init(&v);
	EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, "{ \"name\" : \"lucky\"}"));
	EXPECT_EQ_INT(LEPT_OBJECT, lept_get_type(&v));
	EXPECT_EQ_SIZE_T(1, lept_get_object_size(&v));
	EXPECT_EQ_STRING("name", lept_get_object_key(&v, 0), lept_get_object_key_length(&v, 0));
	lept_free(&v);
#endif // 0

	
#if 1
	lept_init(&v);
	EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, "{ \"name\" : \"lucky\" ,\"age\":21, \"enabled\":true, \"id\":null, \"list\":[1,2,3] }"));
	EXPECT_EQ_INT(LEPT_OBJECT, lept_get_type(&v));
	EXPECT_EQ_SIZE_T(5, lept_get_object_size(&v));
	EXPECT_EQ_STRING("name", lept_get_object_key(&v, 0), lept_get_object_key_length(&v, 0));
	EXPECT_EQ_INT(LEPT_STRING, lept_get_object_member(&v, "name")->v.type);
	EXPECT_EQ_STRING("lucky", lept_get_object_member(&v, "name")->v.u.s.s, 5);
	lept_free(&v);
#endif // 0

	
}

static void test_access_null() {
	lept_value v;
	lept_init(&v);
	lept_set_string(&v, "", 0);
	lept_set_null(&v);
	EXPECT_EQ_INT(LEPT_NULL, lept_get_type(&v));
	lept_free(&v);
}

static void test_access_string() {
	lept_value v;
	lept_init(&v);
	lept_set_string(&v, "", 0);
	EXPECT_EQ_STRING("", lept_get_string(&v), lept_get_string_length(&v));
	lept_set_string(&v, "hello", 5);
	EXPECT_EQ_STRING("hello", lept_get_string(&v), lept_get_string_length(&v));
	lept_free(&v);
}

static void test_access_boolean() {
	lept_value v;
	lept_init(&v);
	lept_set_string(&v, "", 0);
	lept_set_boolean(&v, 0);
	EXPECT_EQ_FALSE(lept_get_boolean(&v));
	lept_set_boolean(&v, 1);
	EXPECT_EQ_TRUE(lept_get_boolean(&v));
	lept_free(&v);
}

static void test_access_number() {
	lept_value v;
	lept_init(&v);
	lept_set_string(&v, "", 0);
	lept_set_number(&v, 3.14);
	EXPECT_EQ_DOUBLE(3.14, lept_get_number(&v));
	lept_free(&v);
}

static void test_parse() {
	test_parse_null();
	test_parse_false();
	test_parse_true();
	test_parse_number();
	test_expect_value();
	test_invalid_value();
	test_not_singular();
	test_parse_string();
	test_parse_array();
	test_parse_object();
	test_stringify_number();
	test_stringify_string();

	test_access_string();
	test_access_null();
	test_access_boolean();
	test_access_number();
	test_parse_number_too_big();
	test_parse_missing_quotation_mark();
}

int main() {
#ifdef _WINDOWS
	_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
#endif // _WINDOWS

	test_parse();
	printf("%d/%d (%3.2f%%) passed\n", test_pass, test_count, test_pass * 100.0 / test_count);
#if 0
	const char* str = "\xF0\x9D\x84\x9E";
	printf("%s\n", str);
#endif // 0

	return main_ret;
}
